package com.tuniu.agents.tools.hotel.tool;

import com.fasterxml.jackson.annotation.JsonClassDescription;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyDescription;
import com.tuniu.agents.tools.hotel.HotelProperties;
import com.tuniu.agents.tools.hotel.constant.HotelConstants;
import com.tuniu.agents.util.HttpManager;
import com.tuniu.agents.util.JsonUtil;
import org.apache.logging.log4j.util.Strings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ai.chat.model.ToolContext;
import org.springframework.beans.factory.annotation.Autowired;

import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.BiFunction;
import java.util.stream.Collectors;

/**
 * Hotel detail tool that retrieves detailed information about hotels
 */
public class HotelDetail implements BiFunction<HotelDetail.Request, ToolContext, HotelDetail.Response> {

    private static final Logger log = LoggerFactory.getLogger(HotelDetail.class);

    public static final String TOOLNAME = "hotel_detail";

    @Autowired
    private HttpManager httpManager;

    private final String url;
    private final String accessKey;

    /**
     * Constructor that initializes the hotel detail URL and access key
     * 
     * @param properties Hotel properties containing configuration
     */
    public HotelDetail(HotelProperties properties) {
        this.url = properties.getHotelDetailUrl();
        this.accessKey = properties.getAccessKey();
    }

    @Override
    public Response apply(Request request, ToolContext toolContext) {
        Request newRequest = null;
        try {
            log.info("HotelDetail input:{}", JsonUtil.toJson(request));
            String conversationId = (String) toolContext.getContext().get("conversation_id");
            String lastAction = (String) toolContext.getContext().get(conversationId + "_LAST_ACTION");
            boolean isPackageQuery = "package_query".equals(lastAction);

            // Build different request parameters based on the previous operation type
            if (isPackageQuery) {
                // Follow-up operation for package query
                newRequest = new Request(
                        request.hotelName(),
                        request.checkIn(),
                        request.checkOut(),
                        isPackageQuery
                );
            } else {
                // Follow-up operation for regular hotel query
                newRequest = new Request(
                        request.hotelName(),
                        request.checkIn(),
                        request.checkOut(),
                        isPackageQuery
                );
            }
            Response response = httpManager.post(url, newRequest, Response.class, Map.of(HotelProperties.KEY_NAME, accessKey));
            return buildNewResponse(response, isPackageQuery);
        } catch (Exception e) {
            log.error("HotelDetail apply error request:{}", JsonUtil.toJson(newRequest), e);
            return null;
        }
    }

    /**
     * Builds a new response with package query information
     * 
     * @param response Original response
     * @param isPackageQuery Flag indicating if this is a package query
     * @return Built response with additional information
     */
    private Response buildNewResponse(Response response, Boolean isPackageQuery) {
        return new Response(
                response.successCode(),
                response.hotelId(),
                response.hotelName(),
                response.checkIn(),
                response.checkOut(),
                response.domestic(),
                response.adultNum(),
                response.childNum(),
                response.roomNum(),
                response.cityCode(),
                response.cityName(),
                isPackageQuery,
                response.commentScore(),
                response.starName(),
                response.business(),
                buildNewRooms(response.rooms(), response.cityName(), response.cityCode())
        );
    }

    /**
     * Builds new room list with city information
     * 
     * @param rooms List of rooms
     * @param cityName City name
     * @param cityCode City code
     * @return List of agent rooms with updated information
     */
    private List<AgentRoom> buildNewRooms(List<AgentRoom> rooms, String cityName, Integer cityCode) {
        return rooms.stream().map(room -> buildNewRoom(room, cityName, cityCode)).collect(Collectors.toList());
    }

    /**
     * Builds a new room with city information
     * 
     * @param room Original room
     * @param cityName City name
     * @param cityCode City code
     * @return Agent room with updated information
     */
    private AgentRoom buildNewRoom(AgentRoom room, String cityName, Integer cityCode) {
        return new AgentRoom(
                room.roomId(),
                room.roomName(),
                room.area(),
                room.floor(),
                room.bedTypeName(),
                room.bedSize(),
                room.window(),
                room.smoking(),
                room.roomCount(),
                room.firstPic(),
                buildNewRatePlans(room.ratePlans(), cityName, cityCode)
        );
    }

    /**
     * Builds new rate plans with city information
     * 
     * @param ratePlans List of rate plans
     * @param cityName City name
     * @param cityCode City code
     * @return List of agent room prices with updated information
     */
    private List<AgentRoomPrice> buildNewRatePlans(List<AgentRoomPrice> ratePlans, String cityName, Integer cityCode) {
        return ratePlans.stream().map(ratePlan -> buildNewRatePlan(ratePlan, cityName, cityCode)).collect(Collectors.toList());
    }

    /**
     * Builds a new rate plan with city information
     * 
     * @param ratePlan Original rate plan
     * @param cityName City name
     * @param cityCode City code
     * @return Agent room price with updated information
     */
    private AgentRoomPrice buildNewRatePlan(AgentRoomPrice ratePlan, String cityName, Integer cityCode) {
        return new AgentRoomPrice(
                ratePlan.vendorRatePlanId(),
                ratePlan.ratePlanName(),
                ratePlan.mealText(),
                ratePlan.cancelDesc(),
                ratePlan.cancelText(),
                ratePlan.rmbPrices(),
                ratePlan.count(),
                ratePlan.preBookParam(),
                ratePlan.packageInfo(),
                buildNewCommonUrl(ratePlan, cityName, cityCode)
        );
    }

    /**
     * Builds a new common URL with booking links
     * 
     * @param roomPrice Room price information
     * @param cityName City name
     * @param cityCode City code
     * @return Common URL with app and mini-program links
     */
    private static CommonUrl buildNewCommonUrl(AgentRoomPrice roomPrice, String cityName, Integer cityCode) {
        return new CommonUrl(
                buildAppPreBookUrl(roomPrice, cityName, cityCode),
                Strings.EMPTY,
                buildMpPreBookUrl(roomPrice, cityName, cityCode)
        );
    }

    /**
     * Builds a pre-booking URL for mobile app
     * 
     * @param roomPrice Room price information
     * @param cityName City name
     * @param cityCode City code
     * @return App pre-booking URL
     */
    public static String buildAppPreBookUrl(AgentRoomPrice roomPrice, String cityName, Integer cityCode) {
        String preBookUrl = "";
        try {
            Map<String, Object> params = buildCommonDetailParams(roomPrice, cityName, cityCode);

            Map<String, Object> urlParams = new HashMap<>();
            urlParams.put("rctModule", "tnhotel");
            urlParams.put("rctModuleName", "hotelBookInfo");
            urlParams.put("rctModuleParams", params);

            String jsonParams = JsonUtil.toJson(urlParams);
            String encodedParams = URLEncoder.encode(jsonParams, StandardCharsets.UTF_8);

            preBookUrl = "tuniuapp://page?iosPageName=TNReactNativeViewController&androidPageName=com.tuniu.app.ui.activity.TNReactNativeActivity&parameters=" + encodedParams;
        } catch (Exception e) {
            log.info("Failed to generate App booking link: ", e);
        }
        return preBookUrl;
    }

    /**
     * Builds a pre-booking URL for mini-program
     * 
     * @param roomPrice Room price information
     * @param cityName City name
     * @param cityCode City code
     * @return Mini-program pre-booking URL
     */
    private static String buildMpPreBookUrl(AgentRoomPrice roomPrice, String cityName, Integer cityCode) {
        String preBookUrl = "";
        try {
            Map<String, Object> params = buildCommonDetailParams(roomPrice, cityName, cityCode);
            String jsonParams = JsonUtil.toJson(params);
            String encodedParams = URLEncoder.encode(jsonParams, StandardCharsets.UTF_8);

            preBookUrl = "/hotel/pages/book/book?params=" + encodedParams;
        } catch (Exception e) {
            log.error("Failed to generate mini-program booking link: ", e);
        }
        return preBookUrl;
    }

    /**
     * Builds common detail parameters for booking URLs
     * 
     * @param roomPrice Room price information
     * @param cityName City name
     * @param cityCode City code
     * @return Map of common parameters
     */
    private static Map<String, Object> buildCommonDetailParams(AgentRoomPrice roomPrice, String cityName, Integer cityCode) {
        Map<String, Object> params = new HashMap<>();
        params.put("source", "ai");
        params.put("preBookKey", roomPrice.preBookParam());
        params.put("rid", HotelConstants.DEFAULT_RID);
        params.put("pid", HotelConstants.DEFAULT_PID);
        params.put("cityName", cityName);
        params.put("cityCode", cityCode);
        return params;
    }

    /**
     * Builds hotel detail markdown for display
     * 
     * @param response Hotel detail response
     * @return Markdown formatted hotel details
     */
    public static String buildHotelDetailMarkdown(HotelDetail.Response response) {
        StringBuilder recommendDetailMarkdown = new StringBuilder();
        if (recommendDetailMarkdown.isEmpty()) {
            recommendDetailMarkdown.append("|酒店名称|房型名称|入住日期|离店日期|床型名称|餐食描述|取消规则描述|价格|套餐名称|套餐描述 |\n");
            recommendDetailMarkdown.append("|--------|--------|--------|--------|--------|--------|--------|--------|--------|-------- |\n");
        }
        List<AgentRoom> rooms = response.rooms();
        for (AgentRoom room : rooms) {
            // Iterate through rate plans
            List<AgentRoomPrice> ratePlans = room.ratePlans();
            if (ratePlans == null || ratePlans.isEmpty()) {
                continue; // Skip this room type if there are no rate plans
            }
            for (AgentRoomPrice ratePlan : ratePlans) {
                // Package information
                PackageInfo packageInfo = ratePlan.packageInfo();
                String packageName = packageInfo != null ? packageInfo.packageName() : "";
                String packageDesc = packageInfo != null ? packageInfo.packageDesc() : "";
                // Build table row
                recommendDetailMarkdown.append("|")
                        .append(response.hotelName).append("|")
                        .append(room.roomName).append("|")
                        .append(response.checkIn).append("|")
                        .append(response.checkOut).append("|")
                        .append(room.bedTypeName).append("|")
                        .append(ratePlan.mealText).append("|")
                        .append(ratePlan.cancelDesc).append("|")
                        .append(ratePlan.rmbPrices).append("|")
                        .append(packageName).append("|")
                        .append(packageDesc).append("|\n");
            }
        }
        return recommendDetailMarkdown.toString();
    }

    @JsonClassDescription("酒店详情查询参数")
    public record Request(
            @JsonProperty(value = "hotelName") @JsonPropertyDescription("酒店名称") String hotelName,
            @JsonProperty(required = true, value = "checkIn") @JsonPropertyDescription("入住日期,格式yyyy-dd,格式YYYY-DD,示例2025-10-01") String checkIn,
            @JsonProperty(required = true, value = "checkOut") @JsonPropertyDescription("离店日期,格式yyyy-dd,格式YYYY-DD,示例2025-10-01") String checkOut,
            @JsonProperty(value = "onlyPackagePrice") @JsonPropertyDescription("是否打包价格") Boolean onlyPackagePrice) {
    }

    @JsonClassDescription("酒店详情返参")
    public record Response(
            @JsonProperty(required = true,
                    value = "successCode") @JsonPropertyDescription("成功标识，true：\"查询成功！\"，false：\"查询失败！\"") Boolean successCode,
            @JsonProperty(required = true,
                    value = "hotelId") @JsonPropertyDescription("酒店ID") Long hotelId,
            @JsonProperty(value = "hotelName") @JsonPropertyDescription("酒店名称") String hotelName,
            @JsonProperty(value = "checkIn") @JsonPropertyDescription("入住日期") String checkIn,
            @JsonProperty(value = "checkOut") @JsonPropertyDescription("离店日期") String checkOut,
            @JsonProperty(value = "domestic") @JsonPropertyDescription("国际国内") Boolean domestic,
            @JsonProperty(value = "adultNum") @JsonPropertyDescription("成人数") Integer adultNum,
            @JsonProperty(value = "childNum") @JsonPropertyDescription("儿童数") Integer childNum,
            @JsonProperty(value = "roomNum") @JsonPropertyDescription("房间数") Integer roomNum,
            @JsonProperty(value = "cityCode") @JsonPropertyDescription("城市code") Integer cityCode,
            @JsonProperty(value = "cityName") @JsonPropertyDescription("城市名称") String cityName,
            @JsonProperty(value = "onlyPackagePrice") @JsonPropertyDescription("是否打包价格") Boolean onlyPackagePrice,
            @JsonProperty(value = "commentScore") @JsonPropertyDescription("评分") Double commentScore,
            @JsonProperty(value = "starName") @JsonPropertyDescription("酒店星级") String starName,
            @JsonProperty(value = "business") @JsonPropertyDescription("商圈") String business,
            @JsonProperty(value = "rooms") @JsonPropertyDescription("房型信息") List<AgentRoom> rooms) {
    }

    @JsonClassDescription("酒店房型数据")
    public record AgentRoom(
            @JsonProperty(value = "roomId") @JsonPropertyDescription("房型ID") String roomId,
            @JsonProperty(value = "roomName") @JsonPropertyDescription("房型名称") String roomName,
            @JsonProperty(value = "area") @JsonPropertyDescription("面积") String area,
            @JsonProperty(value = "floor") @JsonPropertyDescription("楼层") String floor,
            @JsonProperty(value = "bedTypeName") @JsonPropertyDescription("床型名称") String bedTypeName,
            @JsonProperty(value = "bedSize") @JsonPropertyDescription("床型尺寸") String bedSize,
            @JsonProperty(value = "window") @JsonPropertyDescription("窗型") String window,
            @JsonProperty(value = "smoking") @JsonPropertyDescription("烟") String smoking,
            @JsonProperty(value = "roomCount") @JsonPropertyDescription("房间数量") Integer roomCount,
            @JsonProperty(value = "firstPic") @JsonPropertyDescription("首图") String firstPic,
            @JsonProperty(value = "ratePlans") @JsonPropertyDescription("价格数据") List<AgentRoomPrice> ratePlans) {
    }

    @JsonClassDescription("酒店房型价格数据")
    public record AgentRoomPrice(
            @JsonProperty(value = "vendorRatePlanId") @JsonPropertyDescription("价格计划ID") String vendorRatePlanId,
            @JsonProperty(value = "ratePlanName") @JsonPropertyDescription("价格计划名称") String ratePlanName,
            @JsonProperty(value = "mealText") @JsonPropertyDescription("餐食描述") String mealText,
            @JsonProperty(value = "cancelDesc") @JsonPropertyDescription("取消规则描述") String cancelDesc,
            @JsonProperty(value = "cancelText") @JsonPropertyDescription("取消规则描述文本") String cancelText,
            @JsonProperty(value = "rmbPrices") @JsonPropertyDescription("价格") String rmbPrices,
            @JsonProperty(value = "count") @JsonPropertyDescription("库存") Integer count,
            @JsonProperty(value = "preBookParam") @JsonPropertyDescription("预定参数") String preBookParam,
            @JsonProperty(value = "packageInfo") @JsonPropertyDescription("套餐信息") PackageInfo packageInfo,
            @JsonProperty(value = "url") @JsonPropertyDescription("url地址") CommonUrl url) {
    }

    @JsonClassDescription("套餐信息")
    public record PackageInfo(
            @JsonProperty(value = "packageName") @JsonPropertyDescription("套餐名称") String packageName,
            @JsonProperty(value = "packageDesc") @JsonPropertyDescription("套餐描述") String packageDesc,
            @JsonProperty(value = "packageItemList") @JsonPropertyDescription("套餐信息") List<PackageItem> packageItemList) {
    }

    @JsonClassDescription("套餐子项信息")
    public record PackageItem(
            @JsonProperty(value = "itemTypeName") @JsonPropertyDescription("子项类型名称") String itemTypeName,
            @JsonProperty(value = "itemName") @JsonPropertyDescription("子项名称") String itemName,
            @JsonProperty(value = "itemValidDesc") @JsonPropertyDescription("子项有效描述") String itemValidDesc,
            @JsonProperty(value = "giveType") @JsonPropertyDescription("赠送类型") String giveType,
            @JsonProperty(value = "saddress") @JsonPropertyDescription("赠送地址") String saddress,
            @JsonProperty(value = "useRuleList") @JsonPropertyDescription("使用规则") List<String> useRuleList) {
    }

    @JsonClassDescription("url地址")
    public record CommonUrl(
            @JsonProperty(value = "appUrl") @JsonPropertyDescription("appUrl") String appUrl,
            @JsonProperty(value = "mUrl") @JsonPropertyDescription("m站url") String mUrl,
            @JsonProperty(value = "mpUrl") @JsonPropertyDescription("微信小程序Url") String mpUrl) {
    }

}
